package parser

import (
	"dolphin/ast"
	"dolphin/lexer"
	"dolphin/token"
	"fmt"
	"strconv"
)

var precedences = map[token.TokenType]int{
	// precedences就是优先级表  用于将词法单元类型与其优先级相关联
	token.EQ:       EQUALS,
	token.NOT_EQ:   EQUALS,
	token.LT:       LESSGREATER,
	token.GT:       LESSGREATER,
	token.PLUS:     SUM,
	token.MINUS:    SUM,
	token.SLASH:    PRODUCT,
	token.ASTERISK: PRODUCT,
	token.MOD:      PRODUCT,
	token.LPAREN:   CALL,
	token.LBRACKET: INDEX,
}

const (
	_ int = iota
	LOWEST
	EQUALS      // ==
	LESSGREATER // > or <
	SUM         // +
	PRODUCT     // *
	PREFIX      // -X or !X
	CALL        // myFunction(X)”
	INDEX       // array[index]
)

type Parser struct {
	l         *lexer.Lexer // 指向词法分析器实例的指针,通过l.NextToken()不断获取输入的下一个词法单元
	curToken  token.Token  //  指向输入内容的当前词法单元
	peekToken token.Token  //  指向输入内容的下一个词法单元
	// 查看curToken 当前正在检查的词法单元是为了决定下一步怎么做
	// 如果curToken没有提供足够的信息，还需要peekToken来做抉择

	errors []string // 这会在New中初始化，当peekToken的类型与预期不相符的时候，会调用使用peekError向这个字段添加错误信息

	prefixParseFns map[token.TokenType]prefixParseFn
	infixParseFn   map[token.TokenType]infixParseFn
}

func (p *Parser) registerPrefix(tokenType token.TokenType, fn prefixParseFn) {
	p.prefixParseFns[tokenType] = fn
}
func (p *Parser) registerInfix(tokenType token.TokenType, fn infixParseFn) {
	p.infixParseFn[tokenType] = fn
}

func New(l *lexer.Lexer) *Parser { // 语法分析器的构造器，接受参数是一个词法分析器的实例
	p := &Parser{
		l:      l,
		errors: []string{},
	}

	p.nextToken()
	p.nextToken()

	p.prefixParseFns = make(map[token.TokenType]prefixParseFn)
	p.registerPrefix(token.IDENT, p.parseIdentifier)      // 如果遇到了ident类型的词法单元，解析函数就会调用在*Parser上定义的parseIdentifier方法
	p.registerPrefix(token.INT, p.parseIntegerLiteral)    // 遇到integer类型的词法单元，解析函数调用parseIntegerLiteral的方法
	p.registerPrefix(token.BANG, p.parsePrefixExpression) // 遇到-或者！，就调用前缀解析函数
	p.registerPrefix(token.MINUS, p.parsePrefixExpression)
	p.registerPrefix(token.LPAREN, p.parseGroupedExpression)
	p.registerPrefix(token.IF, p.parseIfExpression)
	p.registerPrefix(token.FUNCTION, p.parseFunctionLiteral)
	p.registerPrefix(token.STRING, p.parseStringLiteral)
	p.registerPrefix(token.LBRACKET, p.parseArrayLiteral)
	p.registerPrefix(token.LBRACE, p.parseHashLiteral)

	p.infixParseFn = make(map[token.TokenType]infixParseFn)
	p.registerInfix(token.PLUS, p.parseInfixExpression)
	p.registerInfix(token.MINUS, p.parseInfixExpression)
	p.registerInfix(token.SLASH, p.parseInfixExpression)
	p.registerInfix(token.MOD, p.parseInfixExpression)
	p.registerInfix(token.ASTERISK, p.parseInfixExpression)
	p.registerInfix(token.EQ, p.parseInfixExpression)
	p.registerInfix(token.NOT_EQ, p.parseInfixExpression)
	p.registerInfix(token.LT, p.parseInfixExpression)
	p.registerInfix(token.GT, p.parseInfixExpression)
	p.registerInfix(token.LBRACKET, p.parseIndexExpression)
	p.registerInfix(token.LPAREN, p.parseCallExpression)

	p.registerPrefix(token.TRUE, p.parseBoolean)
	p.registerPrefix(token.FALSE, p.parseBoolean)
	return p
}

func (p *Parser) parseHashLiteral() ast.Expression {
	hash := &ast.HashLiteral{Token: p.curToken}
	hash.Pairs = make(map[ast.Expression]ast.Expression)

	for !p.peekTokenIs(token.RBRACE) {
		p.nextToken()
		key := p.parseExpression(LOWEST)

		if !p.expectPeek(token.COLON) {
			return nil
		}

		p.nextToken()
		value := p.parseExpression(LOWEST)
		hash.Pairs[key] = value

		if !p.peekTokenIs(token.RBRACE) && !p.expectPeek(token.COMMA) {
			return nil
		}
	}
	if !p.expectPeek(token.RBRACE) {
		return nil
	}
	return hash
}

func (p *Parser) parseIndexExpression(left ast.Expression) ast.Expression {
	exp := &ast.IndexExpression{Token: p.curToken, Left: left}
	p.nextToken()
	exp.Index = p.parseExpression(LOWEST)
	if !p.expectPeek(token.RBRACKET) {
		return nil
	}
	return exp
}

func (p *Parser) parseArrayLiteral() ast.Expression {
	array := &ast.ArrayLiteral{Token: p.curToken}
	array.Elements = p.parseExpressionList(token.RBRACKET)
	return array
}
func (p *Parser) parseExpressionList(end token.TokenType) []ast.Expression {
	list := []ast.Expression{}

	if p.peekTokenIs(end) {
		p.nextToken()
		return list
	}
	p.nextToken()
	list = append(list, p.parseExpression(LOWEST))

	for p.peekTokenIs(token.COMMA) {
		p.nextToken()
		p.nextToken()
		list = append(list, p.parseExpression(LOWEST))
	}
	if !p.expectPeek(end) {
		return nil
	}
	return list
}

func (p *Parser) parseCallExpression(function ast.Expression) ast.Expression {
	exp := &ast.CallExpression{Token: p.curToken, Function: function}
	exp.Arguments = p.parseExpressionList(token.RPAREN)
	return exp
}

func (p *Parser) parseCallArguments() []ast.Expression {
	args := []ast.Expression{}
	if p.peekTokenIs(token.RPAREN) {
		p.nextToken()
		return args
	}

	p.nextToken()
	args = append(args, p.parseExpression(LOWEST))

	for p.peekTokenIs(token.COMMA) {
		p.nextToken()
		p.nextToken()
		args = append(args, p.parseExpression(LOWEST))
	}

	if !p.expectPeek(token.RPAREN) {
		return nil
	}
	return args
}

func (p *Parser) parseFunctionLiteral() ast.Expression {
	lit := &ast.FunctionLiteral{Token: p.curToken}

	if !p.expectPeek(token.LPAREN) {
		return nil
	}
	lit.Parameters = p.parseFunctionParameters()

	if !p.expectPeek(token.LBRACE) {
		return nil
	}
	lit.Body = p.parseBlockStatement()
	return lit
}

func (p *Parser) parseFunctionParameters() []*ast.Identifier {
	identifiers := []*ast.Identifier{} // 参数名列表

	if p.peekTokenIs(token.RPAREN) { // 参数列表没有参数
		p.nextToken()
		return identifiers
	}
	p.nextToken() // 跳过左括号

	ident := &ast.Identifier{Token: p.curToken, Value: p.curToken.Literal} // 实例化一个参数并添加到切片中
	identifiers = append(identifiers, ident)

	for p.peekTokenIs(token.COMMA) { // 如果下一个token是逗号
		p.nextToken() // 来到逗号
		p.nextToken() // 来到下一个参数名
		ident := &ast.Identifier{Token: p.curToken, Value: p.curToken.Literal}
		identifiers = append(identifiers, ident)
	}
	if !p.expectPeek(token.RPAREN) { // 如果没有发现右括号正确包裹参数，那么语法错误
		return nil
	}
	return identifiers
}

func (p *Parser) parseIfExpression() ast.Expression {
	expression := &ast.IfExpression{Token: p.curToken}
	if !p.expectPeek(token.LPAREN) { // if (...)
		return nil
	}
	p.nextToken()
	expression.Condition = p.parseExpression(LOWEST)
	if !p.expectPeek(token.RPAREN) {
		return nil
	}
	if !p.expectPeek(token.LBRACE) {
		return nil
	}
	expression.Consequence = p.parseBlockStatement()
	if p.peekTokenIs(token.ELSE) {
		p.nextToken()                    // 跳过 } , 来到else
		if !p.expectPeek(token.LBRACE) { // 查看下一个token是否是左花括号
			return nil
		}
		expression.Alternative = p.parseBlockStatement() // 进行语句的解析
	}
	return expression
}
func (p *Parser) parseBlockStatement() *ast.BlockStatement {
	block := &ast.BlockStatement{Token: p.curToken}
	block.Statements = []ast.Statement{}

	p.nextToken()

	for !p.curTokenIs(token.RBRACE) && !p.curTokenIs(token.EOF) {
		stmt := p.parseStatement()
		if stmt != nil {
			block.Statements = append(block.Statements, stmt)
		}
		p.nextToken()
	}
	return block
}

func (p *Parser) parseIdentifier() ast.Expression {
	return &ast.Identifier{
		Token: p.curToken,
		Value: p.curToken.Literal,
	}
}

func (p *Parser) nextToken() { // 同时前移curToken和peekToken，功能类似于readChar
	p.curToken = p.peekToken      // curToken指向下一个词法单元
	p.peekToken = p.l.NextToken() // peekToken指向新取出来的词法单元
}

func (p *Parser) ParseProgram() *ast.Program {
	/*
		ParseProgram要做的第一件事就是构造ast的根节点  *ast.Program
		然后遍历每一个句子，解析所有内容
		解析完成之后，返回抽象语法树根节点
	*/
	program := &ast.Program{} // ast根节点
	program.Statements = []ast.Statement{}
	for !p.curTokenIs(token.EOF) { // 遍历输入中的每一个词法单元，直至遇到EOF词法单元
		stmt := p.parseStatement() // 每次解析一条语句，返回值一般是一个ast.Statement
		if stmt != nil {
			program.Statements = append(program.Statements, stmt)
		}
		p.nextToken() // 在执行这个代码之前，当前应该指向了逗号
	}
	return program // 所有内容解析完成返回抽象语法树的根节点
}

func (p *Parser) parseStatement() ast.Statement {
	/*
		检查每一个语句token类型，然后引向不同的解析函数去
		Dolphin只有两种语句，一种是let语句，一种是return语句，如果都不是，那么按照表达式进行解析
	*/
	switch p.curToken.Type {
	case token.LET:
		return p.parseLetStatement()
	case token.RETURN:
		return p.parseReturnStatement()
	default:
		return p.parseExpressionStatement()
	}
}

func (p *Parser) parseExpressionStatement() *ast.ExpressionStatement {
	stmt := &ast.ExpressionStatement{ // 首先构造AST节点
		Token: p.curToken,
	}
	stmt.Expression = p.parseExpression(LOWEST) // 通过调用其他解析函数来填充其节点的字段

	if p.peekTokenIs(token.SEMICOLON) { // 最后检查可选的分号
		p.nextToken()
	}
	return stmt
}

func (p *Parser) parseLetStatement() *ast.LetStatement {
	/*
		    对Let语句的解析
			首先构造一个*ast.LetStatement节点，然后调用expectPeek来判断下一个是不是期望的词法单元
			如果是，那么前移词法单元指针。
			首先期望一个IDENT，用于构造一个*ast.Identifier节点
			然后期望一个等号
			然后跳过了一些表达式，直到遇到了分号为止。
	*/
	stmt := &ast.LetStatement{Token: p.curToken}

	if !p.expectPeek(token.IDENT) { // 检查let后面的词法单元是不是标识符
		return nil
	}

	// 如果顺利，当前的指针应该指向了标识符的词法单元，下一个指向了字面量词法单元
	stmt.Name = &ast.Identifier{Token: p.curToken, Value: p.curToken.Literal} // 存储这个标识符，curToken就是IDENT，Value就是标识符的名称

	if !p.expectPeek(token.ASSIGN) { // 检查标识符后面是不是等于号用来赋值
		return nil
	}

	p.nextToken()

	stmt.Value = p.parseExpression(LOWEST)

	for !p.curTokenIs(token.SEMICOLON) {
		p.nextToken()
	}
	/*
		stmt就是let语句，
		token 就是 let
		name 就是一个ident结构体，存储了ident的名称
		value暂时没有赋值
	*/
	return stmt
}

func (p *Parser) curTokenIs(t token.TokenType) bool {
	// 检查当前token类型是不是t
	return p.curToken.Type == t
}

func (p *Parser) peekTokenIs(t token.TokenType) bool {
	// 检查下一个词法单元类型是不是t
	return p.peekToken.Type == t
}

func (p *Parser) expectPeek(t token.TokenType) bool {
	if p.peekTokenIs(t) {
		p.nextToken() // 若发现下一个词法单元类型是t，那么移动词法单元的指针
		return true
	} else {
		p.peekError(t)
		return false
	}
}

func (p *Parser) Errors() []string {
	// 返回语法分析器包含的错误信息列表
	return p.errors
}

func (p *Parser) peekError(t token.TokenType) {
	msg := fmt.Sprintf("expected next token to be %s,got %s instead", t, p.peekToken.Type)
	p.errors = append(p.errors, msg)
}

func (p *Parser) parseReturnStatement() ast.Statement {
	// 当前这个函数，只能新建一个return语句节点，然后存储return词法单元的类型，然后移动指针直到碰见分号
	stmt := &ast.ReturnStatement{Token: p.curToken} // "return"存到词法单元中

	p.nextToken()

	stmt.ReturnValue = p.parseExpression(LOWEST)
	for !p.curTokenIs(token.SEMICOLON) {
		p.nextToken()
	}
	return stmt
}

type (
	prefixParseFn func() ast.Expression               // 前缀解析函数
	infixParseFn  func(ast.Expression) ast.Expression // 函数接受的参数 是解析的中缀运算符左侧的内容  根据定义，前缀运算符左侧为空。
)

func (p *Parser) parseExpression(precedence int) ast.Expression {
	// 检查前缀位置是否有与p.curToken.Type关联的解析函数
	prefix := p.prefixParseFns[p.curToken.Type]
	if prefix == nil { //  将格式化的错误放到语法分析器的errors字段中
		p.noPrefixParseFnError(p.curToken.Type)
		return nil
	}
	leftExp := prefix() // 如果有对应的前缀解析函数，那么就调用它

	for !p.peekTokenIs(token.SEMICOLON) && precedence < p.peekPrecedence() {
		infix := p.infixParseFn[p.peekToken.Type]
		if infix == nil {
			return leftExp
		}

		p.nextToken()

		leftExp = infix(leftExp)
	}

	return leftExp
}

func (p *Parser) parseIntegerLiteral() ast.Expression {
	lit := &ast.IntegerLiteral{
		Token: p.curToken,
	}
	value, err := strconv.ParseInt(p.curToken.Literal, 0, 64)
	if err != nil {
		msg := fmt.Sprintf("could not parse %q as integer", p.curToken.Literal)
		p.errors = append(p.errors, msg)
		return nil
	}
	lit.Value = value
	return lit
}

func (p *Parser) noPrefixParseFnError(t token.TokenType) {
	msg := fmt.Sprintf("no prefix parse function for %s found", t)
	p.errors = append(p.errors, msg)
}

func (p *Parser) parsePrefixExpression() ast.Expression {
	expression := &ast.PrefixExpression{ // 先构建一个AST节点
		Token:    p.curToken,
		Operator: p.curToken.Literal,
	}
	p.nextToken()                                // 为了解析类似于-55之类的前缀表达式，必须消耗多个词法单元，
	expression.Right = p.parseExpression(PREFIX) // 因此该方法在使用p.curToken构建prefixExpression节点后，会前移再次调用parseExpression
	return expression
}

func (p *Parser) parseInfixExpression(left ast.Expression) ast.Expression {
	expression := &ast.InfixExpression{
		Token:    p.curToken,
		Operator: p.curToken.Literal,
		Left:     left,
	}
	precedence := p.curPrecedence()
	p.nextToken()
	expression.Right = p.parseExpression(precedence)
	return expression
}

func (p *Parser) peekPrecedence() int { // 查看下一个Token的优先级
	if p, ok := precedences[p.peekToken.Type]; ok {
		return p
	}

	return LOWEST
}

func (p *Parser) curPrecedence() int { // 查看当前Token的优先级
	if p, ok := precedences[p.curToken.Type]; ok {
		return p
	}
	return LOWEST
}

func (p *Parser) parseBoolean() ast.Expression {
	return &ast.Boolean{Token: p.curToken, Value: p.curTokenIs(token.TRUE)}
}

func (p *Parser) parseGroupedExpression() ast.Expression {
	p.nextToken()
	exp := p.parseExpression(LOWEST)

	if !p.expectPeek(token.RPAREN) {
		return nil
	}
	return exp
}

func (p *Parser) parseStringLiteral() ast.Expression {
	return &ast.StringLiteral{Token: p.curToken, Value: p.curToken.Literal}
}
